#ifndef _HLFS_STORAGE_HELPER_H_
#define _HLFS_STORAGE_HELPER_H_

#include <stdio.h>
#include <time.h>
#include <sys/time.h>
#include <stdint.h>
#include <fcntl.h>
#include "glib.h"
#include "hlfs.h"
#include "storage.h"
#include "address.h"
#include "misc.h"
#include "bs_local.h"
#include "bs_hdfs.h"

static struct back_storage* init_storage_handler(const char* uri,const char *fs_name)
{
    int ret =0;
    struct back_storage* storage = NULL;
    gchar* back_storage_type;
#if 0    
    gchar* fs_dir_path;
    gchar **v  = g_strsplit(uri,"://",2);
    back_storage_type = g_strdup(v[0]);
    fs_dir_path = g_strdup(v[1]);
    g_strfreev(v);
    if( fs_dir_path[0] != '/' ){
        g_message("has hostname , ignore it"); 
        v = g_strsplit(fs_dir_path,"/",2);
        g_free(fs_dir_path);
        fs_dir_path = g_strdup(v[1]);
        g_strfreev(v);
    }
    g_message("fs_dir_path >>> %s\n",fs_dir_path);
    gchar * fs_path = g_build_filename(fs_dir_path,fs_name,NULL);
#else
     char *head=NULL;
     char *hostname=NULL;
     char *dir=NULL;
     int port;
     ret = parse_from_uri(uri,&head,&hostname,&dir,&port);
     if(ret !=0){
         return NULL;
     }
     gchar *fs_path = g_build_filename(dir,fs_name,NULL);
#endif 
     g_message( "loc [fs:%s], [path:%s]\n",fs_name,fs_path);
     if (0==g_strcmp0(head,"local")){
        storage=get_local_storage_ops();
     }else if(0 == g_strcmp0(head,"hdfs")){
        storage = get_hdfs_storage_ops();
     }else{
        g_message( "Error URI \n");
        ret = -1;
        goto out;
    }


    if(0!=storage->bs_fs_connect(storage,uri,fs_name)){
        ret = -1;
        goto out;
        g_message("connect filesystem failed\n");
        return NULL;
    }
out:
    g_free(fs_path);
    g_free(head);
    g_free(hostname);
    g_free(dir);
    if(ret !=0){
       free(storage);
       storage = NULL;
    }
    return storage;
}


static int deinit_storage_handler(struct back_storage * storage){
    if(storage == NULL){
       g_message("%s storage NULL\n",__func__);
       return 0;
    }
    if(0!=storage->bs_fs_disconnect(storage)){
       return -1;
    }
    g_message("%s disconnect succ\n",__func__);
    //g_free(storage->storage_name);
    //g_message("%s disconnect succ\n",__func__);
    //g_free(storage->fs_name);
    //g_free(storage->fs_location);
    //g_message("%s disconnect succ\n",__func__);
    //g_free(storage->user);
    g_message("%s over\n",__func__);
    return 0;
}

/*
 * get_last_inode_addr: get the segment's lastest log's inode address.
 * @para ctrl: the global control structure of HLFS.
 * @para segfile: the segment file name.
 * @return: if error happens return -1, if right return the inode address.
 */
static uint64_t get_last_inode_storage_addr_in_seg( struct back_storage * storage, uint64_t segno) 
{

    g_message( "enter func %s\n",__func__);
#if 1
	bs_file_t file = NULL;
	bs_file_info_t *info = NULL;
	struct inode_map_entry im;
    gchar * segfile = build_segfile_name(segno);
	if (0 == storage->bs_file_is_exist(storage, segfile)) {
		if (NULL == (file = storage->bs_file_open(storage, segfile, BS_READONLY))) {
			g_message("%s -- open segfile error!\n", __func__);
			return -1;
		}
		if (NULL == (info = storage->bs_file_info(storage, segfile))) {
			storage->bs_file_close(storage, file);
			g_message("%s -- get segfile info error!\n", __func__);
			g_free(segfile);
			return -1;
		}
		g_message("%s -- segfile size:------------>%lld\n", __func__, info->size);
		if (sizeof(struct inode_map_entry)  != storage->bs_file_pread(storage, file, 
							(char*)&im, sizeof(struct inode_map_entry) , info->size - sizeof(struct inode_map_entry) )) {
			storage->bs_file_close(storage, file);
			g_message("%s -- pread error!\n", __func__);
			g_free(segfile);
			return -1;
		}
	} else {
		g_message("%s -- segfile not exist!\n", __func__);
		g_free(segfile);
		return -1;
	}
	storage->bs_file_close(storage, file);
	g_message("im.inode_no:%lld,im.inode_addr:%lld(segno:%u,offset:%u)\n", im.inode_no,im.inode_addr,get_segno(im.inode_addr),get_offset(im.inode_addr));
	g_free(segfile);
	return im.inode_addr;
#endif
}

static int get_cur_latest_segment_info(struct back_storage * storage,uint32_t *segno,uint32_t *offset){
    g_message( "enter func %s\n",__func__);
    int ret =0;
    uint32_t num_entries;
    bs_file_info_t * infos = storage->bs_file_list_dir(storage,".",&num_entries);
    if(infos == NULL){
       g_message("can not get fs:%s seg entries\n",storage->fs_name);
       return -1;
    }
    g_message("how much file :%d\n",num_entries);
    int i=0;
    const char* latest_file = NULL;
    uint64_t latest_st_mtime = 0;
    bs_file_info_t * info = infos;
    for(i=0;i<num_entries;i++){
        g_message("file:%s,size:%d,time:%ld\n",info->name,info->size,info->lmtime);  
        if(g_str_has_suffix(info->name,"seg")){
             if(latest_st_mtime < info->lmtime){
                latest_st_mtime = info->lmtime;
                latest_file = info->name;
                *offset = info->size;
#if 1
                if(info->size == 0){
                    g_message("meta data .................. not update ?\n");
                    bs_file_t file = storage->bs_file_open(storage,info->name,BS_READONLY); 
                    if(file==NULL){
                        g_message("can not open segment file %s\n",info->name);
                        ret = -1;
                        goto out;
                    }
                    /*  read 64M for detect where is last offset */
                    char *tmp_buf = (char*)g_malloc0(1024*1024*64);
                    int count = storage->bs_file_pread(storage,file,tmp_buf,1024*1024*64,0);
                    g_message("read content lent for file:%s is %d\n",info->name,count);
                    g_free(tmp_buf);
                    *offset = count;
                    g_assert(count!=0);
                }
#endif
            }
        } 
        info++;
    }

    if(latest_file!=NULL){
        g_message("latest file:%s\n",latest_file);
        const gchar *basename = g_basename(latest_file);
        gchar **v = g_strsplit(basename,".",2);
        *segno = atol(v[0]);
        g_message("segno :%d\n",*segno);
        g_strfreev(v);
    }
out:
    free(infos);
    return ret;   
}


/*
 * load_inode: load the inode into RAM.
 * @para ctrl: the global control of hlfs.
 * @para segfile: the segment file name.
 * @para inode_addr: the inode address.
 * @return: if error happens return NULL, if right return inode's RAM address.
 */
static struct inode *load_inode(struct back_storage * storage,uint64_t inode_storage_addr)
{
    g_message( "enter func %s\n",__func__);
	bs_file_t file = NULL;
	struct inode *inode = (struct inode*)g_malloc0(sizeof(struct inode));
	if (!inode) {
		g_message("%s -- Allocate Error!\n", __func__);
		return NULL;
	}
	uint32_t offset = get_offset(inode_storage_addr); 
    gchar * segfile = build_segfile_name(get_segno(inode_storage_addr));
	
	g_message("%s -- inode_addr %lld,offset %u\n", __func__, inode_storage_addr,offset);
	if (0 == storage->bs_file_is_exist(storage, segfile)) {
		if (NULL == (file = storage->bs_file_open(storage, segfile, BS_READONLY))) {
			g_free(inode);
			g_free(segfile);
			g_message("%s -- open segfile error!\n", __func__);
			return NULL;
		}
		if (sizeof(struct inode) != storage->bs_file_pread(storage, file,(char*)inode, sizeof(struct inode),offset)) {
			g_free(inode);
			storage->bs_file_close(storage, file);
			g_free(segfile);
			g_message("%s -- pread error!\n", __func__);
			return NULL;
		}
	} else {
		g_message("%s -- segfile not exist!\n", __func__);
		g_free(inode);
		g_free(segfile);
		return NULL;
	}
	return inode;
}


static struct inode * load_latest_inode(struct back_storage *storage) {
    g_message( "enter func %s\n",__func__);
    uint32_t segno;
    uint32_t offset;
    int ret = get_cur_latest_segment_info(storage,&segno,&offset);
    if(ret !=0){
       return NULL;
    }
    g_message( "cur latest segno:%d\n",segno);
    uint64_t inode_storage_addr = get_last_inode_storage_addr_in_seg(storage,segno);
    g_message( "cur latest inode_storage_addr:%llu\n",inode_storage_addr);
    struct inode * latest_inode = load_inode(storage,inode_storage_addr); 
    return latest_inode;
}


/*
 * get_db_addr: get the particular datablock number's address.
 * @para ctrl: the global control structure of hlfs.
 * @para inode: the inode structure.
 * @para db_no: the particular data block number.
 * @return: right return particular db's address, error return -1.
 */
static uint64_t get_db_storage_addr_in_inode(struct back_storage * storage,struct inode *inode, uint64_t db_no,uint32_t block_size) 
{
    g_message( "enter func %s\n",__func__);
	uint64_t cur_storage_addr = 0;
	//guint32 BLOCKSIZE = ctrl->sb.block_size;
    guint32 BLOCKSIZE = block_size;
	if (is_db_in_level1_index_range(db_no)) {
		int idx = db_no % 12;
		cur_storage_addr = inode->blocks[idx];
	} else if (is_db_in_level2_index_range(db_no)) {
		if (0 == inode->iblock) {
			return 1;
		}
		uint64_t *ib = (uint64_t *)read_block(storage, inode->iblock, BLOCKSIZE);
		if (NULL == ib) {
			return -1;
		}
		int idx = (db_no - 12) % 1024;
		cur_storage_addr = *(ib + idx);
		g_free(ib);
	} else if (is_db_in_level3_index_range(db_no)) {
		if(0 == inode->doubly_iblock) {
			return 1;
		}
		uint64_t *ib = (uint64_t *)read_block(storage, inode->doubly_iblock, BLOCKSIZE);
		if (NULL == ib) {
			return -1;
		}
		int idx = (db_no - 12 - 1024) / 1024;
		if (0 == *(ib + idx)) {
			return 1;
		}
		uint64_t *ib2 = (uint64_t *)read_block(storage, *(ib + idx), BLOCKSIZE);
		if (NULL == ib2) {
			return -1;
		}
		uint64_t idx2 = (db_no - 12 - 1024) % 1024;
		cur_storage_addr = *(ib2 + idx2);
		g_free(ib);
		g_free(ib2);
	} else if (is_db_in_level4_index_range(db_no)) {
		if(0 == inode->triply_iblock) {
			return 1;
		}
		uint64_t *ib = (uint64_t *)read_block(storage,inode->triply_iblock, BLOCKSIZE);
		if (NULL == ib) {
			return -1;
		}
		int idx = (db_no - 12 - 1024 - 1024 * 1024) / (1024 * 1024);
		if (0 == *(ib + idx)) {
			return 1;
		}
		uint64_t *ib2 = (uint64_t *)read_block(storage, *(ib + idx), BLOCKSIZE);
		if (NULL == ib2) {
			return -1;
		}
		uint64_t idx2 = (db_no - 12 - 1024 - 1024 * 1024) / 1024 % 1024;
		if (0 == *(ib2 + idx2)) {
			return 1;
		}
		uint64_t *ib3 = (uint64_t *)read_block(storage, *(ib2 + idx2), BLOCKSIZE);
		if (NULL == ib3) {
			return -1;
		}
		uint64_t idx3 = (db_no - 12 - 1024 - 1024 * 1024) % 1024;
		cur_storage_addr = *(ib3 + idx3);
		g_free(ib);
		g_free(ib2);
		g_free(ib3);
	} else {
		g_message("%s -- index error!\n", __func__);
		return -1;
	}
	return cur_storage_addr;
}

static int read_fs_meta(struct back_storage *storage,uint32_t *segment_size,uint32_t *block_size){
    int ret = 0;
    if(0!=storage->bs_file_is_exist(storage,"superblock")){
        g_message( "superblock file can not be accessable \n");
        ret = -1;
    }
    char key_file_buf[4096];
    bs_file_t file = storage->bs_file_open(storage,"superblock",BS_READONLY);
    if(file == NULL){
      g_message("can not open superblock file\n");  
      ret = -1;
    }
    int size = storage->bs_file_pread(storage,file,key_file_buf,4096,0);
    if(size <0){
      g_message("can not read superblock file\n");
      storage->bs_file_close(storage,file);
      ret = -1;
    }
    GKeyFile * sb_keyfile = g_key_file_new();
    if(FALSE == g_key_file_load_from_data(sb_keyfile,key_file_buf,size,G_KEY_FILE_NONE,NULL)){
       g_message( "superblock file format is not key value pairs \n");
       storage->bs_file_close(storage,file);
       g_key_file_free(sb_keyfile);
       ret = -1;
    }
    gchar * _fs_name =  g_key_file_get_string(sb_keyfile,"METADATA","fs_name",NULL);
    guint32 _seg_size = g_key_file_get_integer(sb_keyfile,"METADATA","segment_size",NULL);
    guint32 _block_size = g_key_file_get_integer(sb_keyfile,"METADATA","block_size",NULL);
    if(_fs_name==NULL || _seg_size == 0 || _block_size ==0 ){
       g_message( "superblock parse error \n");
       g_free(_fs_name);
       ret = -3;
       goto out;
    }
	g_message( "superblock : seg size :%u block size :%d fsname :%s\n",_seg_size,_block_size,_fs_name);
    *segment_size = _seg_size;
    *block_size = _block_size;
    if(0!=strcmp(storage->fs_name,_fs_name)){
	   g_message( "error fs name\n");
	   ret = -4;
	   goto out;
	}
out:
    g_key_file_free(sb_keyfile);
    storage->bs_file_close(storage,file);
    return ret;
}
 

static int load_latest_inode_map_entry(struct back_storage *storage,uint64_t segno,uint64_t last_offset,struct inode_map_entry *ime){
   int ret = 0;
   char *segfile_name = build_segfile_name(segno);
   bs_file_t file = storage->bs_file_open(storage,segfile_name,BS_READONLY); 
   if(file==NULL){
      g_message("can not open segment file %s\n",segfile_name);
      return -1; 
   }

   uint64_t inode_map_entry_pos = last_offset - 
                        sizeof(struct inode_map_entry);
   g_message("inode map entry pos %llu\n",inode_map_entry_pos);
   if(sizeof(struct inode_map_entry) != 
                 storage->bs_file_pread(storage,
                 file,(char*)ime,
                 sizeof(struct inode_map_entry),
                 inode_map_entry_pos)){
       g_message("can not read inode map entry \n");
       ret = -1;
       goto out1;
    }
out1:
    storage->bs_file_close(storage,file);
out2:
    g_free(segfile_name);
    //g_free(segfile_path);
    return ret;
}


#endif
